﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace TanksDesk
{

    public partial class Form1 : Form
    {
        public static bool IsTestShot = false;

        #region *** Настройки ******************

        enum MoveRules { MoveThenShot, MoveAndShotInAnyOrder, MoveShotFree }

        static class Sett
        {
            public static int LifeMiddle = 5;
            public static int LifeHavy = 10;

            public static int APntMiddle = 7;
            public static int APntHavy = 4;

            public static int DartsMiddle = 3;
            public static int DartsHavy = 3;

            public static MoveRules MoveRule = MoveRules.MoveShotFree;

            public static bool SingleShotInTank = false; // на выстрел в другой танк тратятся все оставшиеся выстрелы
        }

        #endregion *** Настройки ******************

        enum TankType { Middle, Havy }
        enum Directions { Up, Right, Down, Left }

        class Tank
        {
            public Point Pos;
            public int Hits;
            public TankType tp;
            public Directions Direction;
            public int Side = 1;
            public Bitmap Bmp { get { return bmpL[(int)Direction]; } }

            public bool Dead { get { return Hits <= 0; } }

            Bitmap[] bmpL = new Bitmap[4];

            public Point NextPos
            {
                get
                {
                    switch (Direction)
                    {
                        case Directions.Up: return new Point(Pos.X, Pos.Y - 1);
                        case Directions.Right: return new Point(Pos.X + 1, Pos.Y);
                        case Directions.Down: return new Point(Pos.X, Pos.Y + 1);
                        case Directions.Left: return new Point(Pos.X - 1, Pos.Y);
                    }
                    throw new Exception("ошибка NextPoint. Неизвестное направление");
                }
            }

            public Tank(Point pos, TankType tp, Directions Direction, Bitmap bmp, int Side)
            {
                this.Pos = pos;
                this.tp = tp;
                this.Direction = Direction;
                this.Side = Side;
                switch (tp)
                {
                    case TankType.Middle: Hits = Sett.LifeMiddle; break;
                    case TankType.Havy: Hits = Sett.LifeHavy; break;
                }

                // картинки
                bmpL[0] = ESpace.EBmp.ConvertBMPTo32bit(bmp);
                bmpL[1] = ESpace.EBmp.Rotate90Clockwise(bmpL[0]);
                bmpL[2] = ESpace.EBmp.Rotate90Clockwise(bmpL[1]);
                bmpL[3] = ESpace.EBmp.Rotate90Clockwise(bmpL[2]);
            }
        }

        class Shot
        {
            public Point pos;
            public Directions Direction;
            public TankType Tp;

            public Point NextPos
            {
                get
                {
                    switch (Direction)
                    {
                        case Directions.Up: return new Point(pos.X, pos.Y - 1);
                        case Directions.Right: return new Point(pos.X + 1, pos.Y);
                        case Directions.Down: return new Point(pos.X, pos.Y + 1);
                        case Directions.Left: return new Point(pos.X - 1, pos.Y);
                    }
                    throw new Exception("ошибка NextPoint. Неизвестное направление");
                }
            }

            public Shot(Point pos, Directions Direction, TankType Tp)
            {
                this.pos = pos;
                this.Direction = Direction;
                this.Tp = Tp;
            }
        }

        Shot shot = null;

        enum CellType { Empty, Brick, Wall }

        const int step = 32;
        const int len = 15;

        CellType[,] fld = new CellType[len, len];

        List<Tank> Tanks = new List<Tank>();

        Point Flag1 = new Point(7, 14);
        Point Flag2 = new Point(7, 0);
        bool FlagOver1 = false;
        bool FlagOver2 = false;

        bool GameOver { get { return GameOver1 || GameOver2; } }
       
        bool GameOver1
        {
            get
            {
                if (FlagOver1) return true;
                if (Tanks[0].Hits < 1 && Tanks[1].Hits < 1 && Tanks[2].Hits < 1 && Tanks[3].Hits < 1) return true;
                return false;
            }
        }

        bool GameOver2
        {
            get
            {
                if (FlagOver2) return true;
                if (Tanks[4].Hits < 1 && Tanks[5].Hits < 1 && Tanks[6].Hits < 1 && Tanks[7].Hits < 1) return true;
                return false;
            }
        }

        int GameOverY = int.MinValue;

        int ATankInd = -1;

        int ActionPoints;
        int Shots;
        bool bMove = false;
        bool bShot = false;

        int CntPlayers = 0; // количество ходов игкороков

        static class Bmps
        {
            public static Bitmap Flag1;
            public static Bitmap Flag2;
            public static Bitmap FlagOver1;
            public static Bitmap FlagOver2;
            public static Bitmap GameOver1;
            public static Bitmap GameOver2;
        }

        public Form1()
        {
            InitializeComponent();
            im.Image = new Bitmap(im.Width, im.Height);
            imInfo.Image = new Bitmap(imInfo.Width, imInfo.Height);

            Bmps.Flag1 =  ESpace.EBmp.ConvertBMPTo32bit(Properties.Resources.flag);
            Bmps.Flag2 = ESpace.EBmp.Rotate180(Bmps.Flag1);
            Bmps.FlagOver1 = ESpace.EBmp.ConvertBMPTo32bit(Properties.Resources.FlagOver);
            Bmps.FlagOver2 = ESpace.EBmp.Rotate180(Bmps.FlagOver1);
            Bmps.FlagOver2.SetResolution(Bmps.FlagOver1.HorizontalResolution, Bmps.FlagOver1.VerticalResolution);
            Bmps.GameOver1 = ESpace.EBmp.ConvertBMPTo32bit(Properties.Resources.GameOver);
            Bmps.GameOver2 = ESpace.EBmp.Rotate180(Bmps.GameOver1);
            Bmps.GameOver2.SetResolution(Bmps.GameOver1.HorizontalResolution, Bmps.GameOver1.VerticalResolution);

            NewGame();
        }

        void NewGame()
        {
            Tanks.Clear();
            /*Tanks.Add(new Tank(new Point(00, 00), TankType.Middle, Directions.Down, Properties.Resources.Танк1_1, 1));
            Tanks.Add(new Tank(new Point(05, 00), TankType.Havy, Directions.Down, Properties.Resources.Танк3_1, 1));
            Tanks.Add(new Tank(new Point(09, 00), TankType.Havy, Directions.Down, Properties.Resources.Танк3_1, 1));
            Tanks.Add(new Tank(new Point(14, 00), TankType.Middle, Directions.Down, Properties.Resources.Танк1_1, 1));
            Tanks.Add(new Tank(new Point(00, 14), TankType.Middle, Directions.Up, Properties.Resources.Танк1_2, 2));
            Tanks.Add(new Tank(new Point(05, 14), TankType.Havy, Directions.Up, Properties.Resources.Танк3_2, 2));
            Tanks.Add(new Tank(new Point(09, 14), TankType.Havy, Directions.Up, Properties.Resources.Танк3_2, 2));
            Tanks.Add(new Tank(new Point(14, 14), TankType.Middle, Directions.Up, Properties.Resources.Танк1_2, 2));*/

            Tanks.Add(new Tank(new Point(00, 14), TankType.Middle, Directions.Up, Properties.Resources.Танк1_2, 2));
            Tanks.Add(new Tank(new Point(05, 14), TankType.Havy, Directions.Up, Properties.Resources.Танк3_2, 2));
            Tanks.Add(new Tank(new Point(09, 14), TankType.Havy, Directions.Up, Properties.Resources.Танк3_2, 2));
            Tanks.Add(new Tank(new Point(14, 14), TankType.Middle, Directions.Up, Properties.Resources.Танк1_2, 2));
            Tanks.Add(new Tank(new Point(00, 00), TankType.Middle, Directions.Down, Properties.Resources.Танк1_1, 1));
            Tanks.Add(new Tank(new Point(05, 00), TankType.Havy, Directions.Down, Properties.Resources.Танк3_1, 1));
            Tanks.Add(new Tank(new Point(09, 00), TankType.Havy, Directions.Down, Properties.Resources.Танк3_1, 1));
            Tanks.Add(new Tank(new Point(14, 00), TankType.Middle, Directions.Down, Properties.Resources.Танк1_1, 1));

            LoadData();

            ATankInd = -1;
            SetNextTank();

            FlagOver1 = false;
            FlagOver2 = false;

            CntPlayers = 0;

            GameOverY = int.MinValue;

            ReDrawField();
            //im.Focus();
        }

        void ReDrawField()
        {
            Graphics gr = Graphics.FromImage(im.Image);
            gr.Clear(Color.Black);

            Pen pn = new Pen(Color.FromArgb(49, 49, 49));
            Pen pnSel = new Pen(Color.Orange, 2);
            pnSel.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

            // сетка
            for (int i = 0; i < len; i++)
                for (int j = 0; j < len; j++)
                {
                    gr.DrawRectangle(pn, i * step, j * step, step, step);
                }

            // поле
            for (int i = 0; i < len; i++)
                for (int j = 0; j < len; j++)
                {
                    switch (fld[i, j])
                    {
                        case CellType.Brick: gr.DrawImage(Properties.Resources.Brick, i * step, j * step); break;
                        case CellType.Wall: gr.DrawImage(Properties.Resources.Wall, i * step, j * step); break;
                    }
                }

            // флаги
            gr.DrawImage(FlagOver1 ? Bmps.FlagOver1 : Bmps.Flag1, Flag1.X * step, Flag1.Y * step);
            gr.DrawImage(FlagOver2 ? Bmps.FlagOver2 : Bmps.Flag2, Flag2.X * step, Flag2.Y * step);

            // танки
            for (int i = 0; i < Tanks.Count; i++)
            {
                if (Tanks[i].Dead) continue;

                gr.DrawImage(Tanks[i].Bmp, Tanks[i].Pos.X * step + 1, Tanks[i].Pos.Y * step + 1);
                DrawPanel(i + 1, gr, Tanks[i]);
                // текущий танк
                if (i == ATankInd)
                {
                    gr.DrawRectangle(pnSel, Tanks[i].Pos.X * step + 1, Tanks[i].Pos.Y * step + 1, step - 2, step - 2);
                }
            }

            // выстрел
            if (shot != null)
            {
                int x = shot.pos.X * step + step / 2;
                int y = shot.pos.Y * step + step / 2;
                switch (shot.Direction)
                {
                    case Directions.Up: gr.FillEllipse(Brushes.White, x - 3, y - 3 - ShortN + step / 2, 7, 7); break;
                    case Directions.Right: gr.FillEllipse(Brushes.White, x - 3 + ShortN - step / 2, y - 3, 7, 7); break;
                    case Directions.Down: gr.FillEllipse(Brushes.White, x - 3, y - 3 + ShortN - step / 2, 7, 7); break;
                    case Directions.Left: gr.FillEllipse(Brushes.White, x - 3 - ShortN + step / 2, y - 3, 7, 7); break;
                }
            }

            if (GameOver1) gr.DrawImage(Bmps.GameOver1, 200, GameOverY);
            if (GameOver2) gr.DrawImage(Bmps.GameOver2, 200, GameOverY);

            pn.Dispose();
            pnSel.Dispose();
            gr.Dispose();
            im.Refresh();

            DrawInfo();
        }

        Font fnt = new Font("Arial", 8, FontStyle.Bold);
        Font fnt1 = new Font("Arial", 7, FontStyle.Bold);

        Brush brPnl = new SolidBrush(Color.FromArgb(200, Color.White));

        void DrawPanel(int num, Graphics gr, Tank tnk)
        {
            gr.FillRectangle(brPnl, tnk.Pos.X * step + 5, tnk.Pos.Y * step + 10, step - 10, step - 20);
            gr.DrawImage(Properties.Resources.heart12, tnk.Pos.X * step + 5, tnk.Pos.Y * step + 10);
            gr.DrawString(tnk.Hits.ToString(), fnt1, Brushes.White, tnk.Pos.X * step + 6, tnk.Pos.Y * step + 10);
            gr.DrawString(tnk == Tanks[ATankInd] ? ActionPoints.ToString() : "-", fnt, Brushes.Black, tnk.Pos.X * step + 16, tnk.Pos.Y * step + 9);
        }

        Font fntInfo = new Font("Atial", 10, FontStyle.Bold);

        void DrawInfo()
        {
            Graphics gr = Graphics.FromImage(imInfo.Image);
            gr.Clear(Color.Black);
            gr.DrawImage(Properties.Resources.Keys, 0, 0, 200, 140);

            Tank tnk = Tanks[ATankInd];

            gr.DrawString("Сходилось игроков: " + CntPlayers.ToString(), fntInfo, Brushes.Wheat, 10, 160);
            gr.DrawString("Осталось ходов: " + ActionPoints.ToString(), fntInfo, Brushes.Wheat, 10, 180);
            gr.DrawString("Осталось выстрелов: " + Shots.ToString(), fntInfo, Brushes.Wheat, 10, 200);

            gr.Dispose();
            imInfo.Refresh();
        }

        void SaveData()
        {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < len; j++)
            {
                for (int i = 0; i < len; i++)
                {
                    sb.Append(((int)fld[i, j]).ToString());
                }
                sb.Append("\r\n");
            }
            File.WriteAllText(Application.StartupPath + "\\Save.txt", sb.ToString(), Encoding.UTF8);
        }

        void LoadData()
        {
            ClearFld();
            if (!File.Exists(Application.StartupPath + "\\Save.txt")) return;
            string[] sl = File.ReadAllLines(Application.StartupPath + "\\Save.txt", Encoding.UTF8);
            for (int j = 0; j < len; j++)
            {
                for (int i = 0; i < len; i++)
                {
                    fld[i, j] = (CellType)Convert.ToInt32(sl[j][i].ToString());
                }
            }
        }

        void ClearFld()
        {
            for (int i = 0; i < len; i++)
                for (int j = 0; j < len; j++)
                    fld[i, j] = CellType.Empty;
        }

        private void im_MouseDown(object sender, MouseEventArgs e)
        {
            int x = e.X / step;
            int y = e.Y / step;
            if (x >= len || y >= len) return;

            if (tBtnEdit.Checked)
            {
                if (tBtnBrick.Checked)
                {
                    if (fld[x, y] == CellType.Brick) fld[x, y] = CellType.Empty;
                    else fld[x, y] = CellType.Brick;
                }

                if (tBtnWall.Checked)
                {
                    if (fld[x, y] == CellType.Wall) fld[x, y] = CellType.Empty;
                    else fld[x, y] = CellType.Wall;
                }

                if (tBtnClear.Checked)
                {
                    fld[x, y] = CellType.Empty;
                }

                SaveData();
                ReDrawField();
            }
        }

        private void tBtnFigure_Click(object sender, EventArgs e)
        {
            tBtnBrick.Checked = false;
            tBtnWall.Checked = false;
            tBtnTank.Checked = false;
            tBtnClear.Checked = false;
            ((ToolStripButton)sender).Checked = true;
        }

        private void tBtnEdit_Click(object sender, EventArgs e)
        {
            tBtnBrick.Enabled = tBtnEdit.Checked;
            tBtnWall.Enabled = tBtnEdit.Checked;
            tBtnTank.Enabled = tBtnEdit.Checked;
            tBtnClear.Enabled = tBtnEdit.Checked;
        }

        bool IsBorder(int x, int y)
        {
            if (x < 0 || y < 0 || x >= len || y >= len) return true;
            return false;
        }

        int GetTankInd(int x, int y)
        {
            for (int i = 0; i < Tanks.Count; i++) if (i != ATankInd && !Tanks[i].Dead && Tanks[i].Pos.X == x && Tanks[i].Pos.Y == y) return i;
            return -1;
        }

        bool IsObstracle(Point pnt) { return IsObstracle(pnt.X, pnt.Y); }

        bool IsObstracle(int x, int y)
        {
            return IsBorder(x, y) || fld[x, y] != CellType.Empty || GetTankInd(x, y) != -1;
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (GameOver) return;

            Tank tnk = Tanks[ATankInd];

            // перемещение
            if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Right || e.KeyCode == Keys.Down || e.KeyCode == Keys.Left)
            {
                Directions drct = Directions.Up;
                if (e.KeyCode == Keys.Right) drct = Directions.Right;
                if (e.KeyCode == Keys.Down) drct = Directions.Down;
                if (e.KeyCode == Keys.Left) drct = Directions.Left;

                if (tnk.Direction == drct)
                {
                    if (ActionPoints > 0)
                    {
                        if (!IsObstracle(tnk.NextPos))
                        {
                            tnk.Pos = tnk.NextPos;
                            ActionPoints--;
                            bMove = true;

                            // правило: если до этого стреляли, убираем остальные выстрелы
                            if (Sett.MoveRule == MoveRules.MoveAndShotInAnyOrder && bShot) Shots = 0;


                            ReDrawField();
                        }
                    }
                }
                else { tnk.Direction = drct; ReDrawField(); }
            }
            // выстрел
            else if (e.KeyCode == Keys.Space)
            {
                if (shot == null && Shots > 0)
                {
                    ExecuteShot(tnk.NextPos, tnk.Direction, tnk.tp);
                }
            }
            // смена игрока
            else if (e.KeyCode == Keys.Tab)
            {
                bool bNext = true;
                if (ActionPoints > 0 || Shots > 0)
                    if (MessageBox.Show("У игрока еще не закончены действия, перейти к другому?", "Переход к другому игроку", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.No)
                        bNext = false;

                if (bNext)
                {
                    SetNextTank();
                    ReDrawField();
                }
            }
        }

        void SetNextTank()
        {
            for (int i = 0; i < Tanks.Count; i++)
            {
                int n = i + ATankInd + 1;
                if (n >= Tanks.Count) n -= Tanks.Count;
                if (!Tanks[n].Dead)
                {
                    ATankInd = n;
                    ActionPoints = Tanks[n].tp == TankType.Havy ? Sett.APntHavy : Sett.APntMiddle;
                    Shots = Tanks[n].tp == TankType.Havy ? Sett.DartsHavy : Sett.DartsMiddle;
                    CntPlayers++;
                    bMove = false;
                    bShot = false;
                    break;
                }
            }
        }

        void ExecuteShot(Point pos, Directions Direction, TankType Tp)
        {
            shot = new Shot(pos, Direction, Tp);
            ShortN = 0;

            // правило: если до этого передвигались, убираем дальнейшее передвижение
            if (Sett.MoveRule == MoveRules.MoveThenShot) ActionPoints = 0;
            else if (Sett.MoveRule == MoveRules.MoveAndShotInAnyOrder) if (bMove) ActionPoints = 0;

            bShot = true;
            tmrMain.Start();
        }

        int ShortN = 0;
        private void tmrMain_Tick(object sender, EventArgs e)
        {
            tmrMain.Stop();

            if (shot != null)
            {
                // проверяем на препятствие
                if (IsObstracle(shot.pos) || shot.pos == Flag1 || shot.pos == Flag2)
                {
                    int ind = GetTankInd(shot.pos.X, shot.pos.Y);
                    // танк
                    if (ind != -1)
                    {
                        Tanks[ind].Hits -= ShotTank(Tanks[ind].tp);
                        shot = null;
                    }
                    // граница
                    else if (IsBorder(shot.pos.X, shot.pos.Y))
                    {
                        shot = null;
                        Shots--;
                    }
                    // кирпич
                    else if (fld[shot.pos.X, shot.pos.Y] == CellType.Brick)
                    {
                        fld[shot.pos.X, shot.pos.Y] = CellType.Empty;
                        shot = null;
                        Shots--;
                    }
                    // штаб1
                    else if (shot.pos == Flag1)
                    {
                        FlagOver1 = true;
                        shot = null;
                        Shots--;
                    }
                    // штаб2
                    else if (shot.pos == Flag2)
                    {
                        FlagOver2 = true;
                        shot = null;
                        Shots--;
                    }
                    // другое препятствие
                    else
                    {
                        shot = null;
                        Shots--;
                    }
                }
                else
                {
                    ShortN += step;
                    if (ShortN >= step) { ShortN = 0; shot.pos = shot.NextPos; }
                }
            }

            if (GameOver)
            {
                if (GameOverY == int.MinValue) GameOverY = GameOver1 ? im.Height : -((int)(Bmps.GameOver2.Height / 2) * 2);
                else if (GameOverY != 220) GameOverY += GameOver1 ? -2 : 2;
            }

            ReDrawField();

            if (shot != null || GameOver) tmrMain.Start();
        }

        int ShotTank(TankType tnkTp)
        {
            FrmShot frm = new FrmShot();
            int[] rets = frm.ShowDialog(tnkTp == TankType.Havy ? 90 : 60, 3, Sett.SingleShotInTank ? Shots : 1);
            frm.Dispose();
            int ret = 0;
            for (int i = 0; i < rets.Length; i++) ret += rets[i];

            if (Sett.SingleShotInTank) Shots = 0;
            else Shots--;

            return ret;
        }

        private void im_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            if (IsTestShot)
            {
                FrmShot frm = new FrmShot();
                frm.ShowDialog(50, 4, 10);
                frm.Dispose();
                Close();
            }
        }

        private void tBtnNew_Click(object sender, EventArgs e)
        {
            NewGame();
        }

        private void tBtnHelp_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start(Application.StartupPath + "\\Help.txt");
        }

    }

}
